package Aula11;

import java.util.ArrayList;

public class ServicosFibonacciImpl implements ServicosFibonacci{

	private ArrayList<Long> memo;
	private long tempoIterativo;
	private long tempoRecursivo;
	private long tempoProgDinamica;
	
	public ServicosFibonacciImpl(){
		memo = new ArrayList<Long>();
		tempoIterativo = -1;
		tempoProgDinamica = -1;
		tempoRecursivo = -1;
	}
	
	private long iterativo(int termo){
		long proximo, ultimo, atual;
		ultimo = 0;
		atual = 0;
		proximo = 1;
		for (int i = 2; i < termo + 1; i++) {
			ultimo = atual;
			atual = proximo;
			proximo = ultimo + atual;
		}
		return atual;
	}
	
	private long progDinamica(int termo){
		if (termo <= memo.size()){
			return memo.get(termo - 1);
		}
		if (termo == 1){
			memo.add(0, (long) 0);
			return 0;
		}
		if (termo == 2){
			
			if(memo.isEmpty())
				memo.add((long) 0);
			
			memo.add((long) 1);
			return 1;
		}
		memo.add(progDinamica(termo - 2) + progDinamica(termo - 1));
		return memo.get(termo - 1);
	}
	
	private long recursivo(int termo){
		if (termo == 1) {
			return 0;
		}
		if (termo == 2) {
			return 1;
		}
		return fibonacciRecursivo(termo - 2) + fibonacciRecursivo(termo - 1);
	}
	
	public long fibonacciIterativo(int termo) {
		long inicio, fim, resultado;
		inicio = System.nanoTime();
		resultado = iterativo(termo);
		fim = System.nanoTime();
		tempoIterativo = fim - inicio;
		return resultado;
	}

	public long fibonacciProgDinamica(int termo) {
		long inicio, fim, resultado;
		inicio = System.nanoTime();
		resultado = progDinamica(termo);
		fim = System.nanoTime();
		tempoProgDinamica = fim - inicio;
		return resultado;
	}

	public long fibonacciRecursivo(int termo) {
		long inicio, fim, resultado;
		inicio = System.nanoTime();
		resultado = recursivo(termo);
		fim = System.nanoTime();
		tempoRecursivo = fim - inicio;
		return resultado;
	}

	public long inspecionaMemo(int termo) {
		if(termo > memo.size())
			return -1;
		return memo.get(termo - 1);
	}

	public long ultimoFibDinamica() {
		return tempoProgDinamica;
	}

	public long ultimoFibIterativo() {
		return tempoIterativo;
	}

	public long ultimoFibRecursivo() {
		return tempoRecursivo;
	}
	
	public static void main(String[] args) {
        ServicosFibonacci g = new ServicosFibonacciImpl();
        
        System.out.println(g.inspecionaMemo(1));
        
        int termo = 0; //termo 1 = 0 e termo 2 = 1
        System.out.println(g.fibonacciIterativo(termo));
        System.out.println(g.fibonacciProgDinamica(termo));
        System.out.println(g.fibonacciRecursivo(termo));
        
        System.out.println("Tempos de execucao para termo " + termo);
        System.out.println("iterativo: "+ g.ultimoFibIterativo());
        System.out.println("recursivo: "+ g.ultimoFibRecursivo());
        System.out.println("programacao dinamica: "+ g.ultimoFibDinamica());
        
        System.out.println(g.inspecionaMemo(3));
        System.out.println(g.inspecionaMemo(7)); 
    }
	
}
